diff --git a/src/components.d.ts b/src/components.d.ts index 391c5a6..43b2786 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -1,65 +1,67 @@ /* tslint:disable */ /** * This is an autogenerated file created by the Stencil compiler. * It contains typing information for all components that exist in this project. */ import '@stencil/core'; export namespace Components { interface WikiDataSpinner {} interface WikiDataSpinnerAttributes extends StencilHTMLAttributes {} interface WikidataCaptchaJs {} - interface WikidataCaptchaJsAttributes extends StencilHTMLAttributes {} + interface WikidataCaptchaJsAttributes extends StencilHTMLAttributes { + 'onWikiIsHuman'?: (event: CustomEvent) => void; + } } declare global { interface StencilElementInterfaces { 'WikiDataSpinner': Components.WikiDataSpinner; 'WikidataCaptchaJs': Components.WikidataCaptchaJs; } interface StencilIntrinsicElements { 'wiki-data-spinner': Components.WikiDataSpinnerAttributes; 'wikidata-captcha-js': Components.WikidataCaptchaJsAttributes; } interface HTMLWikiDataSpinnerElement extends Components.WikiDataSpinner, HTMLStencilElement {} var HTMLWikiDataSpinnerElement: { prototype: HTMLWikiDataSpinnerElement; new (): HTMLWikiDataSpinnerElement; }; interface HTMLWikidataCaptchaJsElement extends Components.WikidataCaptchaJs, HTMLStencilElement {} var HTMLWikidataCaptchaJsElement: { prototype: HTMLWikidataCaptchaJsElement; new (): HTMLWikidataCaptchaJsElement; }; interface HTMLElementTagNameMap { 'wiki-data-spinner': HTMLWikiDataSpinnerElement 'wikidata-captcha-js': HTMLWikidataCaptchaJsElement } interface ElementTagNameMap { 'wiki-data-spinner': HTMLWikiDataSpinnerElement; 'wikidata-captcha-js': HTMLWikidataCaptchaJsElement; } export namespace JSX { export interface Element {} export interface IntrinsicElements extends StencilIntrinsicElements { [tagName: string]: any; } } export interface HTMLAttributes extends StencilHTMLAttributes {} } diff --git a/src/components/wiki-captcha-js/wiki-captcha-js.css b/src/components/wiki-captcha-js/wiki-captcha-js.css index 2352291..773df83 100644 --- a/src/components/wiki-captcha-js/wiki-captcha-js.css +++ b/src/components/wiki-captcha-js/wiki-captcha-js.css @@ -1,121 +1,126 @@ :host { font-family: sans-serif; border: 2px solid var(--color-primary, black); margin: 2rem; padding: 1rem; display: block; max-width: 100%; box-shadow: 0 2px 8px rgba(0,0,0,.26); border-radius: 3px; width: 20rem; overflow: hidden; } .wiki-captcha-box { font-family: sans-serif; display: flex; flex-direction: column; align-items: center; max-width: 100%; padding-left: 1rem; padding-right: 1rem; } +.wiki-captcha-error { + color: red; + margin-bottom: 0; +} + .wiki-captcha-img-container { display: flex; flex-wrap: wrap; width: 15rem; max-width: 100%; justify-content: center; border-radius: 3px; overflow: hidden; align-self: center; border: 2px solid var(--color-primary, black); box-shadow: 0 2px 8px rgba(0,0,0,.26); } .wiki-captcha-img, .wiki-captcha-img-selected { width: 100%; height: 6rem; flex: 1 0 30%; /* 3 images per row */ } .wiki-captcha-img-overlay { display: inline-block; position: relative; width: 33.3%; height: 6rem; z-index: 1; cursor: pointer; } .wiki-captcha-img-overlay-on:after { content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: rgba(117, 1, 117, 0.5); } .wiki-captcha-img-overlay-off:after { content: ''; position: absolute; top: 0; right: 0; bottom: 0; left: 0; background: rgba(117, 1, 117, 0.0); } .wiki-captcha-footer { display: flex; align-items: center; padding-top: .5rem; } .wiki-captcha-powered-by { display: flex; justify-content: flex-end; padding-top: .5rem; } .wiki-captcha-powered-by-icon { max-width: 20%; } .wiki-captcha-submit { } button, button:focus { outline: none; } button { font: inherit; padding: 0.25rem 0.5rem; border: 1px solid var(--color-primary, black); background: var(--color-primary, black); color: var(--color-primary-inverse, white); cursor: pointer; } button:hover, button:active { background: var(--color-primary-highlight, grey); border-color: var(--color-primary-highlight, grey); } button:disabled { background: #ccc; border-color: #ccc; color: white; cursor: not-allowed; } p { align-self: center; } diff --git a/src/components/wiki-captcha-js/wiki-captcha-js.tsx b/src/components/wiki-captcha-js/wiki-captcha-js.tsx index 169eb17..30accaf 100644 --- a/src/components/wiki-captcha-js/wiki-captcha-js.tsx +++ b/src/components/wiki-captcha-js/wiki-captcha-js.tsx @@ -1,134 +1,154 @@ -import {Component, State} from '@stencil/core'; +import {Component, EventEmitter, State, Event} from '@stencil/core'; import {AnswerDto, UserAnswerDto, UserAnswersResponseDto, WikiApiDto} from './wiki-api.dto'; @Component({ tag: 'wikidata-captcha-js', styleUrl: './wiki-captcha-js.css', shadow: true }) export class WikiCaptchaJs { private readonly WIKI_DATA_URL = 'http://192.168.1.142:8080'; private readonly WIKI_DATA_ICON = 'https://upload.wikimedia.org/wikipedia/commons/6/66/Wikidata-logo-en.svg'; private readonly IMAGES_PER_CAPTCHA = 9; @State() private questionList: WikiApiDto; @State() private loading = false; + @State() private error = false; + + @Event({bubbles: true, composed: true}) wikiIsHuman: EventEmitter; componentDidLoad() { this.fetchQuestions(); } render() { let question = null; let questionText = null; let images = null; let htmlContent = null; if (this.questionList && this.questionList.questionList.length > 0) { question = this.questionList.questionList[0]; questionText = question.questionText; const maxImageSize = question.answersAvailable.length < this.IMAGES_PER_CAPTCHA ? question.answersAvailable.length : this.IMAGES_PER_CAPTCHA; images = (question.answersAvailable.slice(0, maxImageSize).map(a => (
possible captcha answer
)) ); } - if (this.loading) { + if (this.error) { + htmlContent = [

Oops, something went wrong!

, + this.renderFooter()]; + } + else if (this.loading) { htmlContent = ; } else { htmlContent = [

{questionText}

,
{images}
, - ] + this.renderFooter()] } return (
{htmlContent}
); } + renderFooter() { + let submitHtml = null; + if (!this.error) { + submitHtml = (
+ +
); + } + return ( + ); + } + onImageClick(answer: AnswerDto, event: Event) { if (!answer.userAnswer) { answer.userAnswer = new UserAnswerDto(); answer.userAnswer.selected = false; } answer.userAnswer.selected = !answer.userAnswer.selected; const divElement = event.target as HTMLDivElement; if (answer.userAnswer.selected) { divElement.classList.remove('wiki-captcha-img-overlay-off'); divElement.classList.add('wiki-captcha-img-overlay-on'); } else { divElement.classList.remove('wiki-captcha-img-overlay-on'); divElement.classList.add('wiki-captcha-img-overlay-off'); } } onSubmitCaptchaAnswers() { + this.loading = true; + this.error = false; fetch( this.WIKI_DATA_URL + '/answers', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(this.questionList) } ) .then(res => res.json()) .then((respParsed) => { this.loading = false; + this.error = false; const response = respParsed as UserAnswersResponseDto; - if (response.human) { - alert('You are a human!'); - } else { - alert('You are NOT a human!'); - } + this.wikiIsHuman.emit(response.human); this.fetchQuestions(); }) .catch(err => { console.log(err); this.loading = false; + this.error = true; }); } fetchQuestions() { this.loading = true; + this.error = false; const requestBody = { - 'language': 'en' + 'language': 'en', + 'appid': 1 }; fetch( this.WIKI_DATA_URL + '/questions', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(requestBody) } ) .then(res => res.json()) .then(parsedRes => { this.questionList = parsedRes as WikiApiDto; this.loading = false; + this.error = false; }) .catch(err => { console.log(err); this.loading = false; + this.error = true; }); } } diff --git a/src/index.html b/src/index.html index ad98b37..21752d1 100644 --- a/src/index.html +++ b/src/index.html @@ -1,39 +1,48 @@ Stencil Component Starter